package com.example.shop;

import com.example.shop.es.config.EsProperties;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;

import javax.annotation.PostConstruct;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
@EnableConfigurationProperties(EsProperties.class)
@ComponentScan("com.example.shop.es")
//@SpringBootApplication
public class EsAutoConfiguration {

    @Autowired
    ElasticsearchRestTemplate esTemplate;
    @Autowired
    EsProperties esProperties;


    @PostConstruct
    public void init(){
        List<String> basePackages = esProperties.getBasePackages();
        if(basePackages==null){
            basePackages = new ArrayList<>();
        }
        basePackages.add("com.example.shop.es.repository");
        Set<String> indexSet = new HashSet<>();
        for(String packageName:basePackages){
            //获取指定包下的所有索引类
            Reflections f = new Reflections(packageName);
            //获取该包下@Document注解标记的类
            Set<Class<?>> classSet = f.getTypesAnnotatedWith(Document.class);
            //对所有的该类进行索引的创建
            for (Class<?> clazz : classSet) {
                Document document = clazz.getDeclaredAnnotation(Document.class);
                //将该索引对应的类，保存起来，用于定期删除索引里的数据
                indexSet.add(document.indexName());
                //获取索引操作
                IndexOperations indexOperations = esTemplate.indexOps(clazz);
                //如果允许创建索引且索引不存在，则操作
                if(document.createIndex() && !indexOperations.exists()){
                    org.springframework.data.elasticsearch.core.document.Document settings = org.springframework.data.elasticsearch.core.document.Document.create();
                    settings.put("default_pipeline","my_timestamp_pipeline");
                    //根据setting创建索引
                    indexOperations.create(settings);
                    //并将索引的结构写入刚刚创建的索引中
                    //如果响应为null，一般是因为该索引没有的映射结构没有创建完全，所以建议先存个最大的。
                    indexOperations.putMapping(indexOperations.createMapping(clazz));
                }
            }
        }
        //设置一个定时器，定时监听es里过期的数据
        monitorOverTimeDocument(indexSet);
    }

    /**
     * 设置一个定时任务：每隔一段时间就删除超时的文档
     * 即该文档有超过指定的时间内没有被访问更新，则删除该文档记录
     * @param indexSet 索引set
     */
    public void monitorOverTimeDocument(Set<String> indexSet){
        if(!indexSet.isEmpty()){
            int uselessTimeSeconds = esProperties.getLongUseless();
            int autoDeleteIntervalTime = esProperties.getAutoDeleteIntervalTime();
            String[] indexNames = new String[indexSet.size()];
            indexSet.toArray(indexNames);
            ScheduledExecutorService scheduledThread = new ScheduledThreadPoolExecutor(1);
            scheduledThread.scheduleAtFixedRate(()->{
                try{
                    //转换为毫秒,即只保存距离现在只有uselessTimeMilli时间间隔的数据
                    //这里+8的原因是因为这个时间生成是东八区的，比ingest_timestamp快了8个小时
                    long overDateTimeMilli = LocalDateTime.now().toInstant(ZoneOffset.ofHours(+8)).toEpochMilli()-uselessTimeSeconds*1000;
                    //这个生成的时间也是标准时间，而不是东八区，所以时间是少8小时
                    //在由于ingest_timestamp生成的时间，也是标准时间，也不是东八区，时间也是少8小时。
                    RangeQueryBuilder timeRangeQuery = QueryBuilders.rangeQuery("ingest_timestamp").lte(LocalDateTime.ofInstant(Instant.ofEpochMilli(overDateTimeMilli),ZoneOffset.ofHours(+0)));
                    Query query = new NativeSearchQuery(timeRangeQuery);
                    //删除所有索引中，数据的存放时间超过指定时间
                    esTemplate.delete(query,Object.class, IndexCoordinates.of(indexNames));
                }catch (Exception e){
                    e.printStackTrace();
                }
            },0,autoDeleteIntervalTime, TimeUnit.SECONDS);
        }
    }

}
